/*
 * Copyright (c) 2016 Kevin Herron
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *   http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *   http://www.eclipse.org/org/documents/edl-v10.html.
 */

package org.eclipse.milo.examples.client;

import static org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned.uint;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.config.OpcUaClientConfig;
import org.eclipse.milo.opcua.stack.client.UaTcpStackClient;
import org.eclipse.milo.opcua.stack.core.Stack;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.structured.EndpointDescription;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dayu.unitalk.ua.server.KeyStoreLoader;
import com.dayu.unitalk.ua.server.UnitalkUaServer;

public class ClientExampleRunner {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private final CompletableFuture<OpcUaClient> future = new CompletableFuture<>();

	private final KeyStoreLoader loader = new KeyStoreLoader();

	private final UnitalkUaServer exampleServer;

	private final ClientExample clientExample;

	public ClientExampleRunner(ClientExample clientExample) throws Exception {
		this.clientExample = clientExample;

		exampleServer = new UnitalkUaServer();
		exampleServer.startup().get();
	}

	private OpcUaClient createClient() throws Exception {
		SecurityPolicy securityPolicy = clientExample.getSecurityPolicy();

		EndpointDescription[] endpoints = UaTcpStackClient.getEndpoints("opc.tcp://localhost:12686/unitalk").get();

		EndpointDescription endpoint = Arrays.stream(endpoints)
				.filter(e -> e.getSecurityPolicyUri().equals(securityPolicy.getSecurityPolicyUri())).findFirst()
				.orElseThrow(() -> new Exception("no desired endpoints returned"));

		logger.info("Using endpoint: {} [{}]", endpoint.getEndpointUrl(), securityPolicy);

		loader.load();

		OpcUaClientConfig config = OpcUaClientConfig.builder()
				.setApplicationName(LocalizedText.english("eclipse milo opc-ua client"))
				.setApplicationUri("urn:eclipse:milo:examples:client").setCertificate(loader.getClientCertificate())
				.setKeyPair(loader.getClientKeyPair()).setEndpoint(endpoint)
				.setIdentityProvider(clientExample.getIdentityProvider()).setRequestTimeout(uint(5000)).build();

		return new OpcUaClient(config);
	}

	public void run() {
		future.whenComplete((client, ex) -> {
			if (client != null) {
				try {
					client.disconnect().get();
					exampleServer.shutdown().get();
					Stack.releaseSharedResources();
				} catch (InterruptedException | ExecutionException e) {
					logger.error("Error disconnecting:", e.getMessage(), e);
				}
			} else {
				logger.error("Error running example: {}", ex.getMessage(), ex);
				Stack.releaseSharedResources();
			}

			try {
				Thread.sleep(1000);
				System.exit(0);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		});

		try {
			OpcUaClient client = createClient();

			try {
				clientExample.run(client, future);
				future.get(10, TimeUnit.SECONDS);
			} catch (Throwable t) {
				logger.error("Error running client example: {}", t.getMessage(), t);
				future.complete(client);
			}
		} catch (Throwable t) {
			future.completeExceptionally(t);
		}

		try {
			Thread.sleep(999999999);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

}
